package com.nbsaas.boot.controller.data;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nbsaas.boot.controller.data.batch.ResultBatchHandleImpl;
import com.nbsaas.boot.controller.data.utils.MapUtils;
import com.nbsaas.boot.data.api.apis.QueryDebugApi;
import com.nbsaas.boot.data.api.apis.QueryTemplateApi;
import com.nbsaas.boot.data.api.domain.request.QueryDebugRequest;
import com.nbsaas.boot.data.api.domain.request.QueryTemplateRequest;
import com.nbsaas.boot.data.api.domain.response.QueryTemplateResponse;
import com.nbsaas.boot.data.ext.apis.PreProcessor;
import com.nbsaas.boot.data.ext.apis.ResultBatchHandle;
import com.nbsaas.boot.data.ext.apis.ResultHandleWork;
import com.nbsaas.boot.data.ext.domain.data.HandleData;
import com.nbsaas.boot.data.ext.domain.ext.HandleExt;
import com.nbsaas.boot.data.ext.domain.ext.HandleItem;
import com.nbsaas.boot.data.ext.domain.ext.QueryTemplateExt;
import com.nbsaas.boot.data.ext.handle.ResultDbHandle;
import com.nbsaas.boot.data.utils.FreemarkerUtils;
import com.nbsaas.boot.data.utils.JsqlParserSubQuery;
import com.nbsaas.boot.data.utils.SqlUtils;
import com.nbsaas.boot.model.data.mapper.SqlExeMapper;
import com.nbsaas.boot.model.ext.resource.SqlExeResource;
import com.nbsaas.boot.model.utils.FieldTypeMatch;
import com.nbsaas.boot.model.utils.ObjectMapperUtils;
import com.nbsaas.boot.rest.annotations.RequiresTenant;
import com.nbsaas.boot.rest.annotations.ViewOperator;
import com.nbsaas.boot.rest.api.SqlExeApi;
import com.nbsaas.boot.rest.request.*;
import com.nbsaas.boot.rest.response.ListResponse;
import com.nbsaas.boot.rest.response.MapResponse;
import com.nbsaas.boot.rest.response.PageResponse;
import com.nbsaas.boot.rest.response.ResponseObject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

@RestController
public class DataRestController {


    private final SqlExeApi sqlExeApi;


    @Resource
    private QueryTemplateApi queryTemplateApi;

    @Resource
    private QueryDebugApi queryDebugApi;

    public DataRestController(SqlExeMapper sqlExeMapper) {
        sqlExeApi = new SqlExeResource(sqlExeMapper);
    }


    /**
     * 结果处理
     */
    @Resource
    private Map<String, ResultHandleWork> resultHandleWorks = new HashMap<>();

    /*
     * 前置处理
     */
    @Resource
    private List<PreProcessor> preProcessors = new ArrayList<>();

    @PostMapping("/data/model")
    public ResponseObject<QueryTemplateResponse> view(@RequestBody @Validated(ViewOperator.class) QueryTemplateRequest request) {
        ResponseObject<QueryTemplateResponse> res = queryTemplateApi.view(request);
        if (res.getCode() != 200) {
            return res;
        }
        res.getData().setContent(null);
        res.getData().setStoreProcedure(null);
        res.getData().setResultHandle(null);
        return res;
    }


    @RequiresTenant
    @PostMapping("/data/search")
    public PageResponse<MapResponse> search(HttpServletRequest request) {
        PageResponse<MapResponse> result = new PageResponse<>();
        try {
            ResponseObject<HandleData> obj = getSqlObject(request);
            if (obj.getCode() != 200) {
                result.setCode(obj.getCode());
                result.setMsg(obj.getMsg());
                return result;
            }

            result = sqlExeApi.search(obj.getData().getSqlObject());
            handleList(obj, result);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            result.setCode(501);
            result.setMsg("解析json出错");
            return result;
        }
    }

    /**
     * 数据汇总
     * @param request
     * @return
     */
    @RequiresTenant
    @PostMapping("/data/summary")
    public ResponseObject<MapResponse> summary(HttpServletRequest request) {
        ResponseObject<MapResponse> result = new ResponseObject<>();
        try {
            ResponseObject<HandleData> obj = getSqlObject(request);
            if (obj.getCode() != 200) {
                result.setCode(obj.getCode());
                result.setMsg(obj.getMsg());
                return result;
            }
            if (obj.getData().getAggregates() == null || obj.getData().getAggregates().isEmpty()) {
                result.setCode(501);
                result.setMsg("请配置聚合函数");
                return result;
            }
            String finalSql = JsqlParserSubQuery.summarySql(obj.getData().getSqlObject().getSql(), new ArrayList<>(), obj.getData().getAggregates(), "emp");

            SqlObject sqlObject = new SqlObject();
            sqlObject.setSql(finalSql);
            sqlObject.setQueryType(4);
            return sqlExeApi.selectOne(sqlObject);
        } catch (Exception e) {
            e.printStackTrace();
            result.setCode(501);
            result.setMsg("解析json出错");
            return result;
        }
    }


    @RequiresTenant
    @PostMapping("/data/group")
    public ListResponse<MapResponse> group(HttpServletRequest request) {
        ListResponse<MapResponse> result = new ListResponse<>();
        try {
            ResponseObject<HandleData> obj = getSqlObject(request);
            if (obj.getCode() != 200) {
                result.setCode(obj.getCode());
                result.setMsg(obj.getMsg());
                return result;
            }
            if (obj.getData().getAggregates() == null || obj.getData().getAggregates().isEmpty()) {
                result.setCode(501);
                result.setMsg("请配置聚合函数");
                return result;
            }
            if (obj.getData().getGroups() == null || obj.getData().getGroups().isEmpty()){
                obj.getData().setGroups(new ArrayList<>());
            }
            if (obj.getData().getMeasures() == null || obj.getData().getMeasures().isEmpty()) {
                obj.getData().setMeasures(new ArrayList<>());
            }

            String finalSql = JsqlParserSubQuery.summary(obj.getData().getSqlObject().getSql(), obj.getData().getGroups(), obj.getData().getMeasures(), "emp");

            SqlObject sqlObject = new SqlObject();
            sqlObject.setSql(finalSql);
            sqlObject.setQueryType(4);
            if (obj.getData().getQueryType()==null){
                obj.getData().setQueryType(2);
            }
            if (obj.getData().getQueryType()==1){
                return sqlExeApi.search(sqlObject);
            }else{
                return sqlExeApi.list(sqlObject);
            }
        } catch (Exception e) {
            e.printStackTrace();
            result.setCode(501);
            result.setMsg("解析json出错");
            return result;
        }
    }

    @RequiresTenant
    @PostMapping("/data/batch")
    public ResponseObject<MapResponse> batch(HttpServletRequest request) {
        ResponseObject<MapResponse> result = new ResponseObject<>();
        try {
            ResponseObject<SqlBatchObject> obj = getSqlBatchObject(request);
            if (obj.getCode() != 200) {
                result.setCode(obj.getCode());
                result.setMsg(obj.getMsg());
                return result;
            }
            return sqlExeApi.batch(obj.getData());
        } catch (Exception e) {
            e.printStackTrace();
            result.setCode(501);
            result.setMsg("解析json出错");
            return result;
        }
    }


    @RequiresTenant
    @PostMapping("/data/list")
    public ListResponse<MapResponse> list(HttpServletRequest request) {
        ListResponse<MapResponse> result = new ListResponse<>();
        try {
            ResponseObject<HandleData> obj = getSqlObject(request);
            if (obj.getCode() != 200) {
                result.setCode(obj.getCode());
                result.setMsg(obj.getMsg());
                return result;
            }
            result = sqlExeApi.list(obj.getData().getSqlObject());
            handleList(obj, result);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            result.setCode(501);
            result.setMsg("解析json出错");
            return result;
        }

    }

    private void handleList(ResponseObject<HandleData> obj, ListResponse<MapResponse> result) {

        ResultBatchHandle batchHandle= new ResultBatchHandleImpl();
        batchHandle.setHandle(resultHandleWorks);
        batchHandle.setQueryTemplateApi(queryTemplateApi);
        batchHandle.setSqlExeApi(sqlExeApi);
        batchHandle.handleList(obj, result);
    }

    @RequiresTenant
    @RequestMapping("/data/data/{model}")
    public ListResponse<MapResponse> data(@PathVariable("model") String modelKey) {
        ListResponse<MapResponse> result = new ListResponse<>();
        try {
            if (!StringUtils.hasText(modelKey)) {
                result.setCode(501);
                result.setMsg("无效查询");
                return result;
            }
            //TenantThreadLocal.getTenantKey();
            QueryTemplateRequest req = new QueryTemplateRequest();
            req.setId(modelKey);
            ResponseObject<QueryTemplateResponse> model = queryTemplateApi.view(req);
            if (model.getCode() != 200) {
                result.setCode(501);
                result.setMsg("model不存在");
                return result;
            }
            Map<String, Object> param = new HashMap<>();

            Integer type = model.getData().getType();
            if (type == null) {
                type = 1;
            }
            if (type == 2) {
                //私有查询判断权限
                //私有查询判断权限
                String permission = model.getData().getPermission();
                Subject currentUser = SecurityUtils.getSubject();
                if (StringUtils.hasText(permission) && !currentUser.isPermitted(permission)) {
                    result.setCode(501);
                    result.setMsg("没有权限");
                    return result;
                }
                //查询用户信息

            }
            SqlObject sqlObject = new SqlObject();
            sqlObject.setSql(FreemarkerUtils.process(model.getData().getContent(), param));
            return sqlExeApi.list(sqlObject);
        } catch (Exception e) {
            result.setCode(501);
            result.setMsg("解析json出错");
            return result;
        }

    }

    public ResponseObject<HandleData> getSqlObject(HttpServletRequest request) {
        ResponseObject<HandleData> result = new ResponseObject<>();
        result.setData(new HandleData());
        try {
            SqlObject obj = new SqlObject();
            JSONObject search = JSON.parseObject(request.getInputStream());
            obj.setPage(search.getLongValue("page", 1));
            obj.setSize(search.getLongValue("size", 10));
            String modelKey = search.getString("model");

            QueryTemplateRequest req = new QueryTemplateRequest();
            req.setId(modelKey);
            ResponseObject<QueryTemplateResponse> model = queryTemplateApi.view(req);
            if (model.getCode() != 200) {
                result.setCode(501);
                result.setMsg("model不存在");
                return result;
            }
            QueryTemplateResponse query = model.getData();

            Map<String, Object> param = new HashMap<>();


            if (preProcessors != null) {
                for (PreProcessor preProcessor : preProcessors) {
                    try {
                        preProcessor.process(param);
                    } catch (Exception ignored) {
                    }
                }

            }


            Integer type = query.getType();
            if (type == null) {
                type = 1;
            }
            if (type == 2) {
                //私有查询判断权限
                String permission = query.getPermission();
                Subject currentUser = SecurityUtils.getSubject();
                if (!currentUser.isAuthenticated()) {
                    result.setCode(501);
                    result.setMsg("没有登录");
                    return result;
                }

                if (StringUtils.hasText(permission) && !currentUser.isPermitted(permission)) {
                    result.setCode(502);
                    result.setMsg("没有权限");
                    return result;
                }

                //查询用户信息

            }
            if (search.containsKey("filters")) {
                JSONObject filters = search.getJSONObject("filters");

                QueryTemplateExt ext = model.getData().getExtData();
                if (ext != null && ext.getFilters() != null) {
                    for (QueryFilter filter : ext.getFilters()) {
                        if (filter.getJavaName() != null) {
                            if (filters.containsKey(filter.getJavaName())) {
                                try {
                                    Object temp = FieldTypeMatch.matchType(filter.getJavaName(), filter.getFieldType(), filters);
                                    if (temp != null) {
                                        param.put(filter.getJavaName(), temp);
                                    }

                                    //如果传入值,检查是否必传
                                    Integer required = filter.getRequired();
                                    if (required == null) {
                                        required = 2;
                                    }
                                    if (required == 1) {
                                        if (temp == null) {
                                            result.setCode(501);
                                            result.setMsg("参数" + filter.getFieldName() + "不能为空");
                                            return result;
                                        }
                                    }

                                } catch (Exception ignored) {
                                }
                            } else {
                                //如果没有传入值,检查是否必传
                                Integer required = filter.getRequired();
                                if (required == null) {
                                    required = 2;
                                }
                                if (required == 1) {
                                    result.setCode(501);
                                    result.setMsg("参数" + filter.getFieldName() + "不能为空");
                                    return result;
                                }
                            }
                        }
                    }
                }


                //处理分组和指标
                JSONArray measures = filters.getJSONArray("measures");
                if (measures != null){
                    ObjectMapper mapper = ObjectMapperUtils.getMapper();
                    try {
                        List<QueryFunc> value = mapper.readValue(measures.toJSONString(), new TypeReference<List<QueryFunc>>() {
                        });
                        result.getData().setMeasures(value);
                    }catch (Exception ignored){
                    }
                }
                String[] groups = filters.getObject("groups", String[].class);
                if (groups != null){
                    result.getData().setGroups(Arrays.asList( groups));
                }
                //obj
            }


            //处理前置查询
            Integer beforeHandle = query.getBeforeHandle();
            if (beforeHandle == null) {
                beforeHandle = -1;
            }
            if (beforeHandle == 1) {
                HandleExt extList = query.getHandleExt();
                if (extList != null) {
                    List<HandleItem> items = extList.getItems();
                    if (items != null) {
                        for (HandleItem item : items) {

                            if ("object".equals(item.getResultType())) {
                                SqlObject preSql = new SqlObject();
                                preSql.setSql(FreemarkerUtils.process(item.getSql(), param));
                                preSql.setQueryType(4);
                                ResponseObject<MapResponse> res = sqlExeApi.selectOne(preSql);
                                if (res.getCode() == 200) {
                                    param.put(item.getName(), res.getData());
                                }
                            } else {
                                SqlObject preSql = new SqlObject();
                                preSql.setSql(FreemarkerUtils.process(item.getSql(), param));
                                preSql.setQueryType(2);
                                ListResponse<MapResponse> res = sqlExeApi.list(preSql);
                                if (res.getCode() == 200) {
                                    param.put(item.getName(), res.getData());
                                }
                            }

                        }
                    }
                }
            }

            //预处理sql
            Integer preType = query.getPreType();
            if (preType == null) {
                preType = 2;
            }
            if (preType == 1) {
                param.put("model", modelKey);
                param.put("tableName", MapUtils.sign(param));
                Long page = obj.getPage();
                if (page == null) {
                    page = -1L;
                }
                if (page == 1L) {
                    //执行预处理sql
                    SqlObject preSql = new SqlObject();
                    preSql.setSql(FreemarkerUtils.process(query.getPreSql(), param));
                    preSql.setQueryType(6);
                    sqlExeApi.executeSql(preSql);
                }

            }

            obj.setSql(FreemarkerUtils.process(model.getData().getContent(), param));
            result.getData().setSqlObject(obj);
            result.getData().setResponse(query);
            result.getData().setParam(param);
            if (model.getData().getExtData() != null) {
                result.getData().setAggregates(model.getData().getExtData().getAggregates());
            }
            Integer debugModel = model.getData().getDebugMode();
            if (debugModel == null) {
                debugModel = 2;
            }
            if (debugModel == 1) {
                QueryDebugRequest debugRequest = new QueryDebugRequest();
                debugRequest.setParam(JSON.toJSONString(param));
                debugRequest.setDebugInfo(obj.getSql());
                debugRequest.setQueryTemplate(model.getData().getId());
                debugRequest.setAddDate(new Date());
                debugRequest.setLastDate(new Date());
                queryDebugApi.createData(debugRequest);
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            result.setCode(501);
            result.setMsg("解析json出错");
            return result;
        }
    }


    public ResponseObject<Map<String, Object>> getParam(JSONObject search, QueryTemplateResponse query) {
        ResponseObject<Map<String, Object>> result = new ResponseObject<>();
        Integer type = query.getType();
        if (type == null) {
            type = 1;
        }
        if (type == 2) {
            //私有查询判断权限
            String permission = query.getPermission();
            if (!StringUtils.hasText(permission)) {
                permission = query.getId();
            }
            Subject currentUser = SecurityUtils.getSubject();
            if (!currentUser.isPermitted(permission)) {
                result.setCode(501);
                result.setMsg("没有权限");
                return result;
            }


        }

        Map<String, Object> param = new HashMap<>();


        param.put("tableName", "nb_temp_" + UUID.randomUUID().toString().replace("-", ""));


        if (search.containsKey("filters")) {
            JSONObject filters = search.getJSONObject("filters");

            QueryTemplateExt ext = query.getExtData();
            if (ext != null && ext.getFilters() != null) {
                for (QueryFilter filter : ext.getFilters()) {
                    if (filter.getJavaName() != null) {
                        if (filters.containsKey(filter.getJavaName())) {
                            try {
                                Object temp = FieldTypeMatch.matchType(filter.getJavaName(), filter.getFieldType(), filters);
                                if (temp != null) {
                                    param.put(filter.getJavaName(), temp);
                                }

                                //如果传入值,检查是否必传
                                Integer required = filter.getRequired();
                                if (required == null) {
                                    required = 2;
                                }
                                if (required == 1) {
                                    if (temp == null) {
                                        result.setCode(501);
                                        result.setMsg("参数" + filter.getFieldName() + "不能为空");
                                        return result;
                                    }
                                }

                            } catch (Exception ignored) {
                            }
                        } else {
                            //如果没有传入值,检查是否必传
                            Integer required = filter.getRequired();
                            if (required == null) {
                                required = 2;
                            }
                            if (required == 1) {
                                result.setCode(501);
                                result.setMsg("参数" + filter.getFieldName() + "不能为空");
                                return result;
                            }
                        }
                    }
                }
            }
        }

        result.setData(param);
        return result;

    }


    public ResponseObject<SqlBatchObject> getSqlBatchObject(HttpServletRequest request) {
        ResponseObject<SqlBatchObject> result = new ResponseObject<>();
        try {
            JSONObject search = JSON.parseObject(request.getInputStream());
            String modelKey = search.getString("model");
            QueryTemplateRequest req = new QueryTemplateRequest();
            req.setId(modelKey);
            ResponseObject<QueryTemplateResponse> model = queryTemplateApi.view(req);
            if (model.getCode() != 200) {
                result.setCode(501);
                result.setMsg("model不存在");
                return result;
            }
            QueryTemplateResponse query = model.getData();


            ResponseObject<Map<String, Object>> res = getParam(search, query);
            res.getData().put("page", search.getLongValue("page", 1));
            res.getData().put("size", search.getLongValue("size", 10));

            if (res.getCode() != 200) {
                result.setCode(res.getCode());
                result.setMsg(res.getMsg());
                return result;
            }
            //

            SqlBatchObject sqlBatchObject = query.getSqlBatchObject();
            if (sqlBatchObject == null) {
                result.setCode(501);
                result.setMsg("sqlBatchObject不存在");
                return result;
            }
            Integer execType = sqlBatchObject.getExecType();
            if (execType == null) {
                sqlBatchObject.setExecType(1);
            }
            List<SqlObject> sqlList = sqlBatchObject.getSqlObjects();
            if (sqlList != null) {
                for (SqlObject sqlObject : sqlList) {
                    sqlObject.setSql(FreemarkerUtils.process(sqlObject.getSql(), res.getData()));
                }
            }

            result.setData(sqlBatchObject);
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            result.setCode(501);
            result.setMsg("解析json出错");
            return result;
        }
    }

    @RequiresTenant
    @PostMapping("/data/executeSql")
    public ResponseObject<Integer> executeSql(HttpServletRequest request) {
        ResponseObject<Integer> result = new ResponseObject<Integer>();
        ResponseObject<HandleData> obj = getSqlObject(request);
        if (obj.getCode() != 200) {
            result.setCode(obj.getCode());
            result.setMsg(obj.getMsg());
            return result;
        }

        return sqlExeApi.executeSql(obj.getData().getSqlObject());
    }

    @RequiresTenant
    @PostMapping("/data/selectOne")
    public ResponseObject<MapResponse> selectOne(HttpServletRequest request) {
        ResponseObject<MapResponse> result = new ResponseObject<>();
        ResponseObject<HandleData> obj = getSqlObject(request);
        if (obj.getCode() != 200) {
            result.setCode(obj.getCode());
            result.setMsg(obj.getMsg());
            return result;
        }


        result = sqlExeApi.selectOne(obj.getData().getSqlObject());
        if (result.getData() != null) {
            ListResponse<MapResponse> list = new ListResponse<>();
            list.setData(new ArrayList<>());
            list.getData().add(result.getData());
            handleList(obj, list);
        }
        return result;
    }


    @RequiresTenant
    @PostMapping("/data/exec")
    public ResponseObject<Integer> exec(HttpServletRequest request) {
        ResponseObject<Integer> result = new ResponseObject<>();
        ResponseObject<HandleData> obj = getSqlObject(request);
        if (obj.getCode() != 200) {
            result.setCode(obj.getCode());
            result.setMsg(obj.getMsg());
            return result;
        }
        return sqlExeApi.executeSql(obj.getData().getSqlObject());
    }


}
